#include <iostream>
#include <string.h>

using std::cout;
using std::endl;
using std::string;

/*
    delete called on non-final 'Base' that has virtual functions but non-virtual destructor
    这个错误是在使用 delete 删除一个具有虚函数但没有虚析构函数的非最终类时发生的。
    
    在 C++ 中，如果一个类中包含虚函数，通常应该为这个类定义一个虚析构函数。
    这样做的原因是，
    当使用 delete 操作符删除一个基类指针指向的派生类对象时，
    如果基类的析构函数不是虚函数，那么只会调用基类的析构函数，
    而不会调用派生类的析构函数，这可能导致资源泄漏或未定义的行为。
*/

class Base{
public:
    Base(const char *pstr)
    : _pbase(new char[strlen(pstr)+1]())
    {
        strcpy(_pbase,pstr);
        cout<<"Base有参构造"<<endl;
    }

    virtual
    ~Base(){
        cout<<"~Base()"<<endl;
        if(_pbase){
            delete []_pbase;
            _pbase=nullptr;
        }
    }
    
    virtual
    void print(){
        cout<<"Base::_pbase="<<_pbase<<endl;
    }

private:
    char *_pbase;
};


class Deriver
: public Base
{
public:
    Deriver(const char *pbase, const char *pderived)
    : Base(pbase)
    , _pderiver(new char[strlen(pderived)+1]())
    {
        cout<<"Deriver有参构造"<<endl;
        strcpy(_pderiver,pderived);
    }


    ~Deriver(){
        cout<<"~Deriver()"<<endl;
        if(_pderiver){
            delete []_pderiver;
            _pderiver=nullptr;
        }
    }

    void print(){
        cout<<"Deriver::_pderiver="<<_pderiver<<endl;
    }


private:
    char *_pderiver;
};


void test(){
    Base *pbase=new Deriver("hello","world");
    pbase->print();

    //delete pbase;  
    //如果直接delete，派生类中的_pderiver没有回收，内存泄漏
    //delete的过程：
    //1、先执行对应类型的析构函数，即pbase->~Base()
    //2、执行operator delete，释放指针所指向的堆空间
    //那么如果第一步调用的是正确的析构函数，这个过程就是对的
    //所以用到多态
    
    delete pbase;
    //直接将基类的虚构函数设为virtual，
    //这时派生类的析构函数也会变成虚函数，且被视为重写
    //别忘了重写的3个要求：相同的函数名、参数列表、返回类型
    //这是C++中唯一一个函数名不一样的重写
    //原因：对于任何一个类，它的析构函数都是唯一的，不能重载
    //而编译器会自动地将其改名为~destructor()，此时满足了重写

    //delete dynamic_cast<Deriver *>(pbase); 
    //解决方法2，向下转换，这样delete时调用的是Deriver的析构函数
    //当派生类的析构函数执行后，基类的析构函数也会自动调用
    //但是如果继承了很多代呢？还不如直接把基类的析构函数设为virtual，一劳永逸
    
    pbase=nullptr;
}
int main()
{   
    test();
    return 0;
}

